package cn.yunyichina.utils.convert;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializeConfig;
import com.alibaba.fastjson.serializer.SimpleDateFormatSerializer;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import org.apache.commons.lang.StringUtils;

import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * json对象转换
 * 所有属性名，包括Map对象的key值，将被转换成小写下划线格式
 *
 * @author adriftor
 */
public class JsonUtils {

    private static SerializeConfig serializeConfig = new SerializeConfig();

    private static final String dateFormat = "yyyy-MM-dd HH:mm:ss";

    private static final char UNDERLINE = '_';

    // cache ObjectMapper
    private static ObjectMapper objectMapper = new ObjectMapper();

    static {
        serializeConfig.put(Date.class, new SimpleDateFormatSerializer(dateFormat));
        // 默认的日期格式
        objectMapper.setDateFormat(new SimpleDateFormat(dateFormat));
        objectMapper.setSerializationInclusion(Include.NON_NULL);//null不输出
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        objectMapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);//驼峰格式转成下划线格式
//		objectMapper.configure(SerializationFeature.INDENT_OUTPUT, true);
    }

    /**
     * 序列化Object，Object可以是POJO，也可以是Collection或数组 java对象变为json字符串
     * 所有key都转变成小写下划线格式
     *
     * @param obj 可以是任何对象
     * @return
     * @throws RuntimeException
     */
    public static String toJsonString(Object obj) {
        try {
            if (obj == null) {
                return null;
            }
            if (obj instanceof String) {
                return (String) obj;
            }
            if (obj instanceof Map) {
                return objectMapper.writeValueAsString(mapKeyTranslate((Map) obj, false, true));
            } else {
                return objectMapper.writeValueAsString(obj);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 将Map对象的字符串格式的key，从驼峰形式转换为小写下划线格式，或下划线转成驼峰格式，，
     * 原map保持不变
     *
     * @param map
     * @param toCameFlag       是否转成驼峰形式，否则转成下划线格式
     * @param recursiveMapFlag 对于值是Map的对象是否递归转换
     * @return 新格式key的Map对象
     * @
     */
    public static Map<String, Object> mapKeyTranslate(Map map, boolean toCameFlag, boolean recursiveMapFlag) {
        Map<String, Object> newMap = new LinkedHashMap<String, Object>();
        Iterator<Map.Entry> it = map.entrySet().iterator();
        String newKey;
        Object oldKey;
        while (it.hasNext()) {
            Map.Entry entry = it.next();
            oldKey = entry.getKey();
            if (oldKey != null) {
                if (toCameFlag) {
                    newKey = underlineToCamel(oldKey.toString());
                } else {
                    newKey = camelToUnderline(oldKey.toString());
                }
                Object value = entry.getValue();
                // 对于值是Map对象的继续转换
                if (recursiveMapFlag && value != null && value instanceof Map) {
                    newMap.put(newKey, mapKeyTranslate((Map) value, toCameFlag, recursiveMapFlag));
                } else {
                    newMap.put(newKey, value);
                }
            }
        }
        return newMap;
    }

    /**
     * 转成指定JAVA对象
     *
     * @param jsonString
     * @param clazz
     * @return null
     */
    public static <T> T parseObject(String jsonString, Class<T> clazz) {
        if (StringUtils.isEmpty(jsonString)) { 
            return null;
        }
        try {

            T t = objectMapper.readValue(jsonString, clazz);
//            if (t instanceof Map) {
//            	//将驼峰格式的key转换为下划线格式
//                Iterator<String> it = ((Map) t).keySet().iterator();
//                while (it.hasNext()) {
//                	if (it.next().matches(".*[A-Z]+.*")){
//                		return parseObject(t,clazz);
//                	}
//                }
//                return t;
//            }
            return t;
        } catch (Exception e) {
            throw new RuntimeException("JsonMapper parseObject method throw exception.", e);
        }
    }

    /**
     * 转成指定JAVA对象
     *
     * @param entity
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T parseObject(Object entity, Class<T> clazz) {
        try {
            if (entity == null) {
                return null;
            }
            return objectMapper.readValue(toJsonString(entity), clazz);
        } catch (Exception e) {
            throw new RuntimeException("JsonMapper parseObject method throw exception.", e);
        }
    }

    /**
     * 转成Map<String,Object>对象
     *
     * @param entity
     * @return
     */
    public static JSONObject parseObject(Object entity) {
        try {
            if (entity == null) {
                return null;
            }
            if (entity instanceof String) {
                if (StringUtils.isEmpty((String) entity)) {
                    return null;
                }
                JSONObjectMix returnMap = objectMapper.readValue((String) entity, JSONObjectMix.class);

                //将驼峰格式的key转换为下划线格式
                Iterator<String> it = returnMap.keySet().iterator();
                while (it.hasNext()) {
                    if (it.next().matches(".*[A-Z]+.*")) {
                        return parseObject(returnMap);
                    }
                }
                return returnMap;
            } else {
                return objectMapper.readValue(toJsonString(entity), JSONObjectMix.class);
            }
        } catch (Exception e) {
            throw new RuntimeException("JsonMapper parseObject method throw exception.", e);
        }
    }

    /**
     * 字符串转成JSONArray对象,List<Map>
     *
     * @param strJsonArray []
     * @return null
     */
    public static JSONArray parseArray(String strJsonArray) {
        try {
            if (StringUtils.isEmpty(strJsonArray)) {
                return null;
            }
            return objectMapper.readValue(strJsonArray, JSONArray.class);
        } catch (Exception e) {
            throw new RuntimeException("JsonMapper parseArray method throw exception.", e);
        }
    }

    /**
     * 字符串转成List对象,List<T>
     *
     * @param strJsonArray
     * @param clz
     * @param <T>
     * @return
     */
    public static <T> List<T> parseArray(String strJsonArray, Class<T> clz) {
        try {
            if (StringUtils.isEmpty(strJsonArray)) {
                return null;
            }
            return parseObject(strJsonArray, List.class, clz);
        } catch (Exception e) {
            throw new RuntimeException("JsonMapper parseArray method throw exception.", e);
        }
    }

    /**
     * 反序列化复杂Collection如List<Bean>
     *
     * @param jsonString
     * @param collectionClass 集合类型
     * @param elementClass    集合元素类型
     * @return
     */
    @SuppressWarnings({"unchecked", "rawtypes"})
    public static <T> T parseObject(String jsonString, Class<? extends Collection> collectionClass,
                                    Class<?> elementClass) {
        if (StringUtils.isEmpty(jsonString)) {
            return null;
        }
//		JavaType javaType = objectMapper.getTypeFactory().constru.constructCollectionType(collectionClass, elementClass);
        try {
            return (T) objectMapper.readValue(jsonString, objectMapper.getTypeFactory().constructCollectionType(collectionClass, elementClass));
        } catch (Exception e) {
            throw new RuntimeException("JsonMapper fromJson method throw exception.", e);
        }
    }

    public static String camelToUnderline(String param) {
        if (param == null || "".equals(param.trim())) {
            return "";
        }
        int len = param.length();
        StringBuilder sb = new StringBuilder(len);
        for (int i = 0; i < len; i++) {
            char c = param.charAt(i);
            if (Character.isUpperCase(c)) {
                sb.append(UNDERLINE);
                sb.append(Character.toLowerCase(c));
            } else {
                sb.append(c);
            }
        }
        return sb.toString();
    }

    public static String underlineToCamel(String param) {
        if (param == null || "".equals(param.trim())) {
            return "";
        }
        int len = param.length();
        StringBuilder sb = new StringBuilder(len);
        for (int i = 0; i < len; i++) {
            char c = param.charAt(i);
            if (c == UNDERLINE) {
                if (++i < len) {
                    sb.append(Character.toUpperCase(param.charAt(i)));
                }
            } else {
                sb.append(c);
            }
        }
        return sb.toString();
    }

    public static boolean isJson(String data) {
        try {
            objectMapper.readTree(data);
        } catch (IOException e) {
            return false;
        }
        return true;
    }

    /**
     * 读和写都转变成小写下划线格式
     *
     * @author adriftor
     */
    private static class JSONObjectMix extends JSONObject {

        private static final long serialVersionUID = 1L;

        private String getUnderLineKey(String key) {
            if (key != null) {
                key = JsonUtils.camelToUnderline(key);
            }
            return key;
        }

        public Object get(Object key) {
            if (key instanceof String) {
                return super.get(getUnderLineKey((String) key));
            } else {
                return super.get(key);
            }
        }

        public BigDecimal getBigDecimal(String key) {
            return super.getBigDecimal(getUnderLineKey(key));
        }

        public BigInteger getBigInteger(String key) {
            return super.getBigInteger(getUnderLineKey(key));
        }

        public Boolean getBoolean(String key) {
            return super.getBoolean(getUnderLineKey(key));
        }

        public boolean getBooleanValue(String key) {
            return super.getBooleanValue(getUnderLineKey(key));
        }

        public Byte getByte(String key) {
            return super.getByte(getUnderLineKey(key));
        }

        public byte getByteValue(String key) {
            return super.getByteValue(getUnderLineKey(key));
        }

        public byte[] getBytes(String key) {
            return super.getBytes(getUnderLineKey(key));
        }

        public Date getDate(String key) {
            return super.getDate(getUnderLineKey(key));
        }

        public Double getDouble(String key) {
            return super.getDouble(getUnderLineKey(key));
        }

        public double getDoubleValue(String key) {
            return super.getDoubleValue(getUnderLineKey(key));
        }

        public Float getFloat(String key) {
            return super.getFloat(getUnderLineKey(key));
        }

        public float getFloatValue(String key) {
            return super.getFloatValue(getUnderLineKey(key));
        }

        public int getIntValue(String key) {
            return super.getIntValue(getUnderLineKey(key));
        }

        public Integer getInteger(String key) {
            return super.getInteger(getUnderLineKey(key));
        }

        public JSONArray getJSONArray(String key) {
            return super.getJSONArray(getUnderLineKey(key));
        }

        public JSONObject getJSONObject(String key) {
            return super.getJSONObject(getUnderLineKey(key));
        }

        public Long getLong(String key) {
            return super.getLong(getUnderLineKey(key));
        }

        public long getLongValue(String key) {
            return super.getLongValue(getUnderLineKey(key));
        }

        public <T> T getObject(String key, Class<T> clazz) {
            return super.getObject(key, clazz);
        }

        public Short getShort(String key) {
            return super.getShort(getUnderLineKey(key));
        }

        public short getShortValue(String key) {
            return super.getShortValue(getUnderLineKey(key));
        }

        public java.sql.Date getSqlDate(String key) {
            return super.getSqlDate(getUnderLineKey(key));
        }

        public String getString(String key) {
            return super.getString(getUnderLineKey(key));
        }

        public Timestamp getTimestamp(String key) {
            return super.getTimestamp(getUnderLineKey(key));
        }

        public Object put(String key, Object value) {
            if (key instanceof String) {
                return super.put(key, value);
            } else {
                return super.put(key, value);
            }
        }

        public boolean containsKey(Object key) {
            if (key instanceof String) {
                return super.containsKey(getUnderLineKey((String) key));
            } else {
                return super.containsKey(key);
            }
        }

        public void putAll(Map<? extends String, ? extends Object> m) {
            Iterator<? extends String> it = m.keySet().iterator();
            while (it.hasNext()) {
                String key = it.next();
                this.put(key, m.get(key));
            }
        }
    }

}
